﻿using System;
using System.Collections.Generic;
using AnjLab.SyncIT.Core.Api;
using System.Reflection;
using AnjLab.SyncIT.Core.Data.Outlook;
using Microsoft.Office.Interop.Outlook;

namespace AnjLab.SyncIT.Core.Data._1C
{
    public class Dao : ISourceTaskDao
    {
        private const string NO_1C_ENTERPRISE_INSTALLED = "1С:Предприятие 8.1 не установлено на этом компьютере";

        private readonly Source _source;
        private readonly Type v81ConnectorType;
        private object v81;
        private object v81Connection;

        public Dao(Source source)
        {
            _source = source;

            v81ConnectorType = Type.GetTypeFromProgID("V81.ComConnector");

            if (v81ConnectorType == null)
            {
                Library.Log.Error(NO_1C_ENTERPRISE_INSTALLED);
            }
            else
            {
                v81 = Activator.CreateInstance(v81ConnectorType);

                v81Connection = InvokeObjectMethod(v81, "Connect", new object[] { ConnectionString });
            }
        }

        public void Dispose()
        {
            ReleaseComObject(ref v81Connection);
            ReleaseComObject(ref v81);
        }

        private static void ReleaseComObject(ref object obj)
        {
            if (obj != null && obj.GetType().Name.Equals("__ComObject"))
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
                obj = null;
            }
        }

        private string ConnectionString
        {
            get
            {
                return string.Format("{0};Usr=\"{1}\";Pwd=\"{2}\"",
                                     _source.ProjectBase, _source.UserName, _source.GetPassword());
            }
        }

        public string[] GetChangedTasks()
        {
            var result = new List<string>();
            object v81Documents = null, v81Events = null, v81EventEnumerator = null;
            try
            {
                v81Documents = GetObjectProperty(v81Connection, "Документы");
                v81Events = GetObjectProperty(v81Documents, "Событие");

                //  TODO Выбирать не все, а только свои события
                v81EventEnumerator = InvokeObjectMethod(v81Events, "Выбрать", new Object[] {});

                while ((bool) InvokeObjectMethod(v81EventEnumerator, "Следующий", new Object[] {}))
                {
                    var eventDeletionMark = GetObjectProperty(v81EventEnumerator, "ПометкаУдаления");

                    if (!(bool) eventDeletionMark)
                    {
                        var eventID = GetObjectProperty(v81EventEnumerator, "Номер");

                        result.Add(eventID.ToString());
                    }
                }
            }
            finally
            {
                ReleaseComObject(ref v81EventEnumerator);
                ReleaseComObject(ref v81Events);
                ReleaseComObject(ref v81Documents);
            }

            return result.ToArray();
        }

        public ISourceTask Get(string taskID)
        {
            object v81Documents = null, v81Events = null, v81Event = null;
            object eventPriorityObj_ = null, eventStateObj_ = null, ca = null, eventResponsibleObj_ = null;
            try
            {
                v81Documents = GetObjectProperty(v81Connection, "Документы");
                v81Events = GetObjectProperty(v81Documents, "Событие");
                v81Event = InvokeObjectMethod(v81Events, "НайтиПоНомеру", new Object[] {taskID, DateTime.Now});


                if (v81Event == null)
                    return null;

                var eventDeletionMark = GetObjectProperty(v81Event, "ПометкаУдаления");

                if (!(bool) eventDeletionMark)
                {
                    var result = new Event {ID = taskID};

                    eventPriorityObj_ = GetObjectProperty(v81Event, "Важность");
                    result.Priority =
                        InvokeObjectMethod(v81Connection, "XMLString", new[] {eventPriorityObj_}).ToString();

                    eventStateObj_ = GetObjectProperty(v81Event, "СостояниеСобытия");
                    result.Status = InvokeObjectMethod(v81Connection, "XMLString", new[] {eventStateObj_}).ToString();

                    ca = GetObjectProperty(v81Event, "Контрагент");
                    if (ca != null)
                        result.Contractor = ca.GetType() != typeof (string)
                                                ? GetObjectProperty(ca, "Description").ToString()
                                                : ca.ToString();

                    result.Description = GetObjectProperty(v81Event, "ОписаниеСобытия").ToString();
                    result.Summary = string.Format("{0} {1}", result.Contractor,
                                                   GetObjectProperty(v81Event, "ОписаниеСобытия"));
                    result.Content = GetObjectProperty(v81Event, "СодержаниеСобытия").ToString();

                    eventResponsibleObj_ = GetObjectProperty(v81Event, "Ответственный");
                    result.Owner = GetObjectProperty(eventResponsibleObj_, "Description").ToString();
                    result.OwnerShortName = GetObjectProperty(eventResponsibleObj_, "Code").ToString().Trim();

                    result.StartDate = GetObjectProperty(v81Event, "НачалоСобытия").ToString();
                    result.EndDate = GetObjectProperty(v81Event, "ОкончаниеСобытия").ToString();

                    return result;
                }
            }
            finally
            {
                ReleaseComObject(ref eventPriorityObj_);
                ReleaseComObject(ref eventStateObj_);
                ReleaseComObject(ref ca);
                ReleaseComObject(ref eventResponsibleObj_);

                ReleaseComObject(ref v81Event);
                ReleaseComObject(ref v81Events);
                ReleaseComObject(ref v81Documents);
            }
            return null;
        }

        public bool CanTrackTaskLifetime
        {
            get { return false; }
        }

        public void CheckConnection()
        {
            if (v81ConnectorType == null)
            {
                throw new InvalidOperationException(NO_1C_ENTERPRISE_INSTALLED);
            }
            if (v81 == null)
            {
                throw new InvalidOperationException("Неверные параметры соединения");
            }
        }

        public object GetObjectProperty(object refObject, string propertyName)
        {
            return v81ConnectorType.InvokeMember(propertyName, 
                BindingFlags.GetProperty, 
                null, refObject, null);
        }

        public object SetObjectProperty(object refObject, string propertyName, Object[] NewPropertyValue)
        {
            return v81ConnectorType.InvokeMember(propertyName, 
                BindingFlags.SetProperty, 
                null, refObject, 
                NewPropertyValue);
        }

        public object InvokeObjectMethod(object refObject, string methodName, Object[] parameters)
        {
            return v81ConnectorType.InvokeMember(methodName, 
                BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static, 
                null, refObject, parameters);
        }

        public bool SupportsBackSynchronization
        {
            get { return true; }
        }

        public void MergeTasks(ISourceTask task, IOutlookItem outlookTask)
        {
            if (!(task is Event))
            {
                throw new ArgumentException("Task should be of type 1C event", "task");
            }

            if (outlookTask.Complete && !task.IsClosed)
            {
                //  Close the task in the source: set status to Done
                Library.Log.Debug("Finishing event in 1C with ID = {0}", task.ID);
                CloseEvent(task.ID);
            }

            if (outlookTask.Importance != OutlookTicketDao.ConvertToOutlookImportance(task.Importance))
            {
                //  Update priority in the source
                Library.Log.Debug("Changing event priority to {0} in 1C with ID = {1}",
                                  outlookTask.Importance, task.ID);
                ChangeEventPriority(task.ID, outlookTask.Importance);
            }
        }

        public void CloseEvent(string eventID)
        {
            object v81Documents = null, v81Events = null, v81Event = null;
            object eventObject = null, v81Enums = null, v81EnumsStates = null, v81EnumEnd = null;

            try
            {
                v81Documents = GetObjectProperty(v81Connection, "Документы");
                v81Events = GetObjectProperty(v81Documents, "Событие");
                v81Event = InvokeObjectMethod(v81Events, "НайтиПоНомеру",
                                              new Object[] {eventID, DateTime.Now});

                if (v81Event == null)
                    return;

                eventObject = InvokeObjectMethod(v81Event, "ПолучитьОбъект", new Object[] {});

                v81Enums = GetObjectProperty(v81Connection, "Перечисления");
                v81EnumsStates = GetObjectProperty(v81Enums, "СостоянияСобытий");
                v81EnumEnd = GetObjectProperty(v81EnumsStates, "Завершено");

                SetObjectProperty(eventObject, "СостояниеСобытия", new[] {v81EnumEnd});
                InvokeObjectMethod(eventObject, "Записать", new Object[] {});
            }
            finally
            {
                ReleaseComObject(ref v81EnumEnd);
                ReleaseComObject(ref v81EnumsStates);
                ReleaseComObject(ref v81Enums);
                ReleaseComObject(ref eventObject);

                ReleaseComObject(ref v81Event);
                ReleaseComObject(ref v81Events);
                ReleaseComObject(ref v81Documents);
            }
        }

        public void ChangeEventPriority(string eventID, OlImportance newPriority)
        {
            object v81Documents = null, v81Events = null, v81Event = null;
            object eventObject = null, v81Enums = null, v81EnumsPrioritys = null, v81EnumPriority = null;
            try
            {
                v81Documents = GetObjectProperty(v81Connection, "Документы");
                v81Events = GetObjectProperty(v81Documents, "Событие");
                v81Event = InvokeObjectMethod(v81Events, "НайтиПоНомеру",
                                              new Object[] {eventID, DateTime.Now});

                if (v81Event == null)
                    return;

                eventObject = InvokeObjectMethod(v81Event, "ПолучитьОбъект", new Object[] {});

                v81Enums = GetObjectProperty(v81Connection, "Перечисления");
                v81EnumsPrioritys = GetObjectProperty(v81Enums, "Важность");

                switch (newPriority)
                {
                    case OlImportance.olImportanceHigh:
                        v81EnumPriority = GetObjectProperty(v81EnumsPrioritys, "Высокая");
                        break;
                    case OlImportance.olImportanceLow:
                        v81EnumPriority = GetObjectProperty(v81EnumsPrioritys, "Низкая");
                        break;
                    default:
                        v81EnumPriority = GetObjectProperty(v81EnumsPrioritys, "Средняя");
                        break;
                }

                SetObjectProperty(eventObject, "Важность", new[] {v81EnumPriority});
                InvokeObjectMethod(eventObject, "Записать", new Object[] {});
            }
            finally
            {
                ReleaseComObject(ref v81EnumPriority);
                ReleaseComObject(ref v81EnumsPrioritys);
                ReleaseComObject(ref v81Enums);
                ReleaseComObject(ref eventObject);

                ReleaseComObject(ref v81Event);
                ReleaseComObject(ref v81Events);
                ReleaseComObject(ref v81Documents);
            }
        }
    }
}
